//! Nacos客户端主入口
//! 
//! 提供Nacos服务发现、配置管理和健康检查的统一入口
//! 支持HTTP和gRPC双协议，具备高可用和负载均衡能力

use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::info;
use nacos_core::ServiceInstance;

use crate::config::ClientConfig;
use crate::error::{ClientError, ClientResult};
use crate::network::{HttpClient, GrpcClient, HealthChecker};
use crate::naming::NamingClient;
use crate::ConfigClient;

/// Nacos客户端API接口
#[async_trait::async_trait]
pub trait NacosClientApi: Send + Sync {
    fn config(&self) -> &ClientConfig;
    fn http_client(&self) -> &HttpClient;
    async fn register_instance(
        &self,
        namespaceId: &str,
        service_name: &str,
        instance: ServiceInstance,
    ) -> ClientResult<()>;
    async fn deregister_instance(
        &self,
        namespace: &str,
        service_name: &str,
        ip: &str,
        port: u16,
    ) -> ClientResult<()>;
    async fn get_instances(
        &self,
        namespace: &str,
        service_name: &str,
        healthy_only: bool,
    ) -> ClientResult<Vec<ServiceInstance>>;
    async fn list_services(
        &self,
        namespace: &str,
        page: u32,
        page_size: u32,
    ) -> ClientResult<Vec<String>>;
    async fn send_heartbeat(
        &self,
        namespace: &str,
        service_name: &str,
        instance: ServiceInstance,
    ) -> ClientResult<()>;
    async fn get_config(
        &self,
        namespace: &str,
        group: &str,
        data_id: &str,
    ) -> ClientResult<String>;
    async fn publish_config(
        &self,
        namespace: &str,
        group: &str,
        data_id: &str,
        content: &str,
    ) -> ClientResult<bool>;
    async fn remove_config(
        &self,
        namespace: &str,
        group: &str,
        data_id: &str,
    ) -> ClientResult<bool>;
    async fn list_configs(
        &self,
        namespace: &str,
        group: &str,
        page: u32,
        page_size: u32,
    ) -> ClientResult<Vec<String>>;
}

/// Nacos客户端
/// 
/// 核心客户端结构，负责协调所有Nacos功能模块
/// 包含HTTP客户端、gRPC客户端、健康检查器等组件
/// 
/// # 设计原则
/// - 线程安全：所有组件都是线程安全的
/// - 高可用：支持故障转移和重试机制
/// - 可扩展：支持插件式扩展
#[derive(Debug, Clone)]
pub struct NacosClient {
    /// 客户端配置，包含服务器地址、认证信息等
    config: ClientConfig,
    /// HTTP客户端，用于REST API调用
    http_client: Arc<HttpClient>,
    /// gRPC客户端，用于高性能通信
    grpc_client: Arc<RwLock<Option<GrpcClient>>>,
    /// 健康检查器，监控服务器状态
    health_checker: HealthChecker,
}

impl NacosClient {
    /// 创建新的Nacos客户端
    pub async fn new(config: ClientConfig) -> ClientResult<Self> {
        info!("Creating Nacos client with config: {:?}", config);
        
        let http_client = Arc::new(HttpClient::new(config.clone())?);
        let health_checker = HealthChecker::new(config.clone())?;
        
        // 延迟创建gRPC客户端，需要时再初始化
        let grpc_client = Arc::new(RwLock::new(None));

        Ok(Self {
            config,
            http_client,
            grpc_client,
            health_checker,
        })
    }

    /// 获取gRPC客户端
    pub async fn grpc_client(&self) -> ClientResult<GrpcClient> {
        let mut client = self.grpc_client.write().await;
        
        if client.is_none() {
            let new_client = GrpcClient::new(self.config.clone()).await?;
            *client = Some(new_client);
        }
        
        Ok(client.as_ref().unwrap().clone())
    }

    /// 创建命名服务客户端
    pub fn naming_client(&self) -> NamingClient {
        NamingClient::new(
            Arc::new(self.clone()),
            self.config.namespace.clone(),
        )
    }

    /// 创建配置服务客户端
    pub fn config_client(&self) -> ConfigClient {
        ConfigClient::new(Arc::new(self.clone()))
    }

    /// 健康检查
    pub async fn health_check(&self) -> ClientResult<bool> {
        self.health_checker.check_health(&self.config.server_addr).await
    }

    /// 关闭客户端
    pub async fn close(&self) {
        info!("Closing Nacos client");
        // 清理资源
        let mut client = self.grpc_client.write().await;
        *client = None;
    }
}

#[async_trait::async_trait]
impl NacosClientApi for NacosClient {
    fn config(&self) -> &ClientConfig {
        &self.config
    }

    fn http_client(&self) -> &HttpClient {
        &self.http_client
    }

    async fn register_instance(
        &self,
        namespaceId: &str,
        service_name: &str,
        instance: ServiceInstance,
    ) -> ClientResult<()> {
        let url = format!(
            "/nacos/v1/ns/instance?namespaceId={}&serviceName={}",
            namespaceId, service_name
        );

        let params = vec![
            ("ip".to_string(), instance.ip.clone()),
            ("port".to_string(), instance.port.to_string()),
            ("weight".to_string(), instance.weight.to_string()),
            ("healthy".to_string(), instance.healthy.to_string()),
            ("enabled".to_string(), instance.enabled.to_string()),
            ("ephemeral".to_string(), instance.ephemeral.to_string()),
            ("clusterName".to_string(), instance.cluster_name.clone()),
            ("metadata".to_string(), serde_json::to_string(&instance.metadata)?),
        ];

        let response = self.http_client.post(&url, &params).await?;
        let result: String = response.json().await?;
        
        if result == "ok" {
            Ok(())
        } else {
            Err(ClientError::ServerError(result))
        }
    }

    async fn deregister_instance(
        &self,
        namespace: &str,
        service_name: &str,
        ip: &str,
        port: u16,
    ) -> ClientResult<()> {
        let url = format!(
            "/nacos/v1/ns/instance?namespaceId={}&serviceName={}&ip={}&port={}",
            namespace, service_name, ip, port
        );

        let response = self.http_client.delete(&url, &[]).await?;
        let result: String = response.json().await?;
        
        if result == "ok" {
            Ok(())
        } else {
            Err(ClientError::ServerError(result))
        }
    }

    async fn get_instances(
        &self,
        namespace: &str,
        service_name: &str,
        healthy_only: bool,
    ) -> ClientResult<Vec<ServiceInstance>> {
        let url = format!(
            "/nacos/v1/ns/instance/list?namespaceId={}&serviceName={}&healthyOnly={}",
            namespace, service_name, healthy_only
        );

        let response = self.http_client.get(&url, &[]).await?;
        let result: serde_json::Value = response.json().await?;
        
        let hosts = result.get("hosts")
            .and_then(|v| v.as_array())
            .ok_or_else(|| ClientError::InvalidResponse("Missing hosts field".to_string()))?;

        let mut instances = Vec::new();
        for host in hosts {
            let instance: ServiceInstance = serde_json::from_value(host.clone())?;
            instances.push(instance);
        }

        Ok(instances)
    }

    async fn list_services(
        &self,
        namespace: &str,
        page: u32,
        page_size: u32,
    ) -> ClientResult<Vec<String>> {
        let url = format!(
            "/nacos/v1/ns/service/list?namespaceId={}&pageNo={}&pageSize={}",
            namespace, page, page_size
        );

        let response = self.http_client.get(&url, &[]).await?;
        let result: serde_json::Value = response.json().await?;
        
        let services = result.get("serviceNames")
            .and_then(|v| v.as_array())
            .ok_or_else(|| ClientError::InvalidResponse("Missing serviceNames field".to_string()))?;

        let mut service_names = Vec::new();
        for service in services {
            if let Some(name) = service.as_str() {
                service_names.push(name.to_string());
            }
        }

        Ok(service_names)
    }

    async fn send_heartbeat(
        &self,
        namespace: &str,
        service_name: &str,
        instance: ServiceInstance,
    ) -> ClientResult<()> {
        let url = format!(
            "/nacos/v1/ns/instance/beat?namespace={}&serviceName={}",
            namespace, service_name
        );

        let params = vec![
            ("ip".to_string(), instance.ip.clone()),
            ("port".to_string(), instance.port.to_string()),
            ("weight".to_string(), instance.weight.to_string()),
            ("healthy".to_string(), instance.healthy.to_string()),
            ("enabled".to_string(), instance.enabled.to_string()),
            ("clusterName".to_string(), instance.cluster_name.clone()),
            ("metadata".to_string(), serde_json::to_string(&instance.metadata)?),
        ];

        let response = self.http_client.put(&url, &params).await?;
        let result: String = response.json().await?;
        
        if result == "ok" {
            Ok(())
        } else {
            Err(ClientError::ServerError(result))
        }
    }

    async fn get_config(
        &self,
        namespace: &str,
        group: &str,
        data_id: &str,
    ) -> ClientResult<String> {
        let url = format!(
            "/nacos/v1/cs/configs?namespaceId={}&group={}&dataId={}",
            namespace, group, data_id
        );

        let response = self.http_client.get(&url, &[]).await?;
        let content: String = response.text().await?;
        
        Ok(content)
    }

    async fn publish_config(
        &self,
        namespace: &str,
        group: &str,
        data_id: &str,
        content: &str,
    ) -> ClientResult<bool> {
        let url = "/nacos/v1/cs/configs";
        
        let params = vec![
            ("namespaceId".to_string(), namespace.to_string()),
            ("group".to_string(), group.to_string()),
            ("dataId".to_string(), data_id.to_string()),
            ("content".to_string(), content.to_string()),
        ];

        let response = self.http_client.post(url, &params).await?;
        let result: String = response.text().await?;
        
        Ok(result == "true")
    }

    async fn remove_config(
        &self,
        namespace: &str,
        group: &str,
        data_id: &str,
    ) -> ClientResult<bool> {
        let url = "/nacos/v1/cs/configs";
        
        let params = vec![
            ("namespaceId".to_string(), namespace.to_string()),
            ("group".to_string(), group.to_string()),
            ("dataId".to_string(), data_id.to_string()),
        ];

        let response = self.http_client.delete(url, &params).await?;
        let result: String = response.text().await?;
        
        Ok(result == "true")
    }

    async fn list_configs(
        &self,
        namespace: &str,
        group: &str,
        page: u32,
        page_size: u32,
    ) -> ClientResult<Vec<String>> {
        let url = format!(
            "/nacos/v1/cs/configs?namespaceId={}&group={}&pageNo={}&pageSize={}",
            namespace, group, page, page_size
        );

        let response = self.http_client.get(&url, &[]).await?;
        let result: serde_json::Value = response.json().await?;
        
        let data_ids = result.get("dataIds")
            .and_then(|v| v.as_array())
            .ok_or_else(|| ClientError::InvalidResponse("Missing dataIds field".to_string()))?;

        let mut ids = Vec::new();
        for id in data_ids {
            if let Some(name) = id.as_str() {
                ids.push(name.to_string());
            }
        }

        Ok(ids)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// 模拟Nacos客户端用于测试
    pub struct MockNacosClient {
        config: ClientConfig,
        http_client: Arc<HttpClient>,
    }

    impl MockNacosClient {
        pub fn new() -> Self {
            let config = ClientConfig::default();
            let http_client = Arc::new(HttpClient::new(config.clone()).unwrap());
            Self {
                config,
                http_client,
            }
        }
    }

    #[async_trait::async_trait]
    impl NacosClientApi for MockNacosClient {
        fn config(&self) -> &ClientConfig {
            &self.config
        }

        fn http_client(&self) -> &HttpClient {
            &self.http_client
        }

        async fn register_instance(
            &self,
            _namespace: &str,
            _service_name: &str,
            _instance: ServiceInstance,
        ) -> ClientResult<()> {
            Ok(())
        }

        async fn deregister_instance(
            &self,
            _namespace: &str,
            _service_name: &str,
            _ip: &str,
            _port: u16,
        ) -> ClientResult<()> {
            Ok(())
        }

        async fn get_instances(
            &self,
            _namespace: &str,
            service_name: &str,
            _healthy_only: bool,
        ) -> ClientResult<Vec<ServiceInstance>> {
            Ok(vec![ServiceInstance::new(
                service_name.to_string(),
                "DEFAULT_GROUP".to_string(),
                "127.0.0.1".to_string(),
                8080,
            )])
        }

        async fn list_services(
            &self,
            _namespace: &str,
            _page: u32,
            _page_size: u32,
        ) -> ClientResult<Vec<String>> {
            Ok(vec!["test-service".to_string()])
        }

        async fn get_config(
            &self,
            _namespace: &str,
            _group: &str,
            _data_id: &str,
        ) -> ClientResult<String> {
            Ok("test-config-content".to_string())
        }

        async fn publish_config(
            &self,
            _namespace: &str,
            _group: &str,
            _data_id: &str,
            _content: &str,
        ) -> ClientResult<bool> {
            Ok(true)
        }

        async fn remove_config(
            &self,
            _namespace: &str,
            _group: &str,
            _data_id: &str,
        ) -> ClientResult<bool> {
            Ok(true)
        }

        async fn list_configs(
            &self,
            _namespace: &str,
            _group: &str,
            _page: u32,
            _page_size: u32,
        ) -> ClientResult<Vec<String>> {
            Ok(vec!["test-config".to_string()])
        }
    }


    #[tokio::test]
    async fn test_client_creation() {
        let config = ClientConfig::new("127.0.0.1:8848");
        let client = NacosClient::new(config).await;
        
        // 由于可能没有实际服务器，这里只测试创建是否成功
        // 实际应该mock服务器
        assert!(client.is_ok() || client.is_err());
    }

    #[tokio::test]
    async fn test_mock_client() {
        let mock_client = MockNacosClient::new();
        let instances = mock_client.get_instances("test", "service", true).await;
        assert!(instances.is_ok());
        assert!(!instances.unwrap().is_empty());
    }
}